#include "pal/vzResultParse.h"
#include "userLog.h"
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <stdbool.h>
#include <stdio.h>
#include <math.h>
#include "CxlUtil.h"
#include "semUntil.h"
#include "pal/lane_cmd_parse.h"
#include "common/configParser.h"
#include "uv.h"
#include "hal/imx6/src/hal_gps_uv.h"
#include "voiceChedaoji.h"
#include "pending_data_queue.h"
#include "cJSON.h"
#include "sqlLoop.h"
#include "picDeleteLoop.h"
#include "base64.h"

// 重复车牌间隔
#ifndef VZ_MAX_REPEAT_TIME
#define VZ_MAX_REPEAT_TIME 10 * 1000
#endif

// 等待图片响应最长时间
#define PIC_RECV_RESPON_TIME_OUT 3 * 1000

typedef struct
{
    uv_fs_t req;
    uv_buf_t buf;
} fs_write_t;

#if 0 // xinfenglee
static uv_timer_t vzResultTimer;
static uint16_t lastLicense[TH_LP_LEN] = {0};
#else

typedef struct _recodeLicenseTimer
{
    uv_timer_t vzResultTimer;
    uint16_t lastLicense[TH_LP_LEN];
} recodeLicenseTimer;
#endif

#define MAX_LICENSE_TIMER (3)
static recodeLicenseTimer lastLicenseArray[MAX_LICENSE_TIMER];

static void saveImgs(uint8_t *path, VZ_PACKET_T *img);
static void picBlockFrameOthersInfoInit(pic_block_send_t *picBlock);

static void closeImgcb(uv_fs_t *req);
static void writeImgCb(uv_fs_t *req);
static void openImgCb(uv_fs_t *req);
static void fsyncImgcb(uv_fs_t *req);

static uv_async_t sql2SocketEvent;
static void sql2Socket(uv_async_t *handle);
void sql2SocketUvInit()
{
    uv_async_init(getLoop(), &sql2SocketEvent, sql2Socket);
}

void dispatchSql2SocketEvent()
{
    uv_async_send(&sql2SocketEvent);
}

static void sql2Socket(uv_async_t *handle)
{
    bool isHasSql2Socket = isSql2SocketEmpty();

    Packet *packTmp = NULL;
    while (isHasSql2Socket != true)
    {
        packTmp = sql2SocketDequeue();
        if (packTmp != NULL)
        {
            CamDataStruct *tmp = (CamDataStruct *)packTmp->pkt_data;

            // LOG_INFO("长度%d，id：%d", tmp->camDataLen, tmp->camId);
            // hzlog_info(userLog, tmp->timeBcd, 6);
            vz_work_req_t *wr_rq_send_pic = (vz_work_req_t *)malloc(sizeof(vz_work_req_t));
            wr_rq_send_pic->buf = uv_buf_init((uint8_t *)tmp, sizeof(CamDataStruct));
            uv_queue_work(getLoop(), (uv_work_t *)wr_rq_send_pic, sendPicWork, afterPicSendWorkCb);

            free(packTmp);
        }
        else
        {
            break;
        }
        isHasSql2Socket = isSql2SocketEmpty();
    }
}

static void repeatTimeOutCb0(uv_timer_t *handle)
{
    memset(lastLicenseArray[0].lastLicense, 0, TH_LP_LEN);
}
static void repeatTimeOutCb1(uv_timer_t *handle)
{
    memset(lastLicenseArray[1].lastLicense, 0, TH_LP_LEN);
}
static void repeatTimeOutC2(uv_timer_t *handle)
{
    memset(lastLicenseArray[2].lastLicense, 0, TH_LP_LEN);
}

uv_timer_cb repeatTimeOutCb[MAX_LICENSE_TIMER] = {repeatTimeOutCb0, repeatTimeOutCb1, repeatTimeOutC2};

static bool isIvsResultRepeat(TH_PlateResult *stDisc, int camId)
{
    int index = camId % 3;
    if (memcmp(lastLicenseArray[index].lastLicense, stDisc->license, TH_LP_LEN) == 0)
    {

        uint64_t due_time = uv_timer_get_due_in(&lastLicenseArray[index].vzResultTimer);
        // 推送的车牌重复，并且定时器未未0，跳出
        if (due_time > 0)
        {
            LOG_DEBUG("车牌 %s 重复，暂不处理；剩余 %ld ms后重置", stDisc->license, due_time);
            return true;
        }
    }
    else
    {
        // LOG_INFO("车牌不重复，清除定时器");
        uv_timer_stop(&lastLicenseArray[index].vzResultTimer);
    }
    // 只要车牌来就开始定时器
    uv_timer_start(&lastLicenseArray[index].vzResultTimer, repeatTimeOutCb[index], VZ_MAX_REPEAT_TIME, 0);
    memcpy(lastLicenseArray[index].lastLicense, stDisc->license, TH_LP_LEN);
    return false;
}

/**
 * @param picType 0 小图， 1 大图
 */
static void insertPicToSqlite(uint8_t picName[PIC_FILENAME_MAX_LEN], uint8_t picType, license_data_t *licenseData)
{
    SqlAsyncCmd cmd = {0};

    cmd.cmdType = SQL_CMD_TYPE_ADD;
    cmd.camId = licenseData->cam_id;
    cmd.fileNo = licenseData->license_img_unique_tag;
    cmd.picType = picType;

    memcpy(cmd.picName, picName, PIC_FILENAME_MAX_LEN);
    memcpy(cmd.licese, licenseData->license_str, MEM_SIZE(license_data_t, license_str));
    memcpy(cmd.gps.gps_gngga_data, licenseData->license_gps_gga_data, MEM_SIZE(license_data_t, license_gps_gga_data));
    memcpy(cmd.gps.gps_gnrmc_data, licenseData->license_gps_rmc_data, MEM_SIZE(license_data_t, license_gps_rmc_data));
    memcpy(cmd.shotTimeBcd, licenseData->licese_timeBcd, MEM_SIZE(license_data_t, licese_timeBcd));

    Packet *packTmp = PacketMalloc(sizeof(SqlAsyncCmd));
    copy_packet(packTmp, sizeof(SqlAsyncCmd), (uint8_t *)&cmd);
    sqlEnqueue(packTmp);
    dispatchSqlCmdExec();
}

static void insertCalibrationToSqlite(uint8_t picName[PIC_FILENAME_MAX_LEN], calibration_v2_data_t *calibrationData)
{
    SqlAsyncCmd cmd = {0};

    cmd.cmdType = SQL_CMD_TYPE_ADD;
    cmd.camId = calibrationData->cam_id;
    cmd.fileNo = calibrationData->calibration_v2_img_unique_tag;
    cmd.picType = CALIBRATION_PIC_TYPE;

    memcpy(cmd.picName, picName, PIC_FILENAME_MAX_LEN);
    memcpy(cmd.gps.gps_gngga_data, calibrationData->calibration_v2_gps_gga_data, MEM_SIZE(calibration_v2_data_t, calibration_v2_gps_gga_data));
    memcpy(cmd.gps.gps_gnrmc_data, calibrationData->calibration_v2_gps_rmc_data, MEM_SIZE(calibration_v2_data_t, calibration_v2_gps_rmc_data));
    memcpy(cmd.shotTimeBcd, calibrationData->calibration_v2_timebcd, MEM_SIZE(calibration_v2_data_t, calibration_v2_timebcd));

    Packet *packTmp = PacketMalloc(sizeof(SqlAsyncCmd));
    copy_packet(packTmp, sizeof(SqlAsyncCmd), (uint8_t *)&cmd);
    sqlEnqueue(packTmp);
    dispatchSqlCmdExec();
}

static uint8_t picNums = 0;
static void licenseDataInit(license_data_t *license_data, TH_PlateResult *stDisc, VzAppStruct *vzAppStruct, uint8_t id, uint8_t unique_tag)
{
    memset(license_data, 0, sizeof(license_data_t));
    headInit((head_t *)license_data, 0x74);
    memcpy(license_data->license_str, stDisc->license, TH_LP_LEN);
    time_bcd(license_data->licese_timeBcd);
    license_data->license_img_unique_tag = unique_tag;
    license_data->license_reliable_percent = stDisc->nConfidence;
    license_data->license_color = stDisc->nColor;
    license_data->license_type = stDisc->nType;
    license_data->cam_id = id;
    memcpy(license_data->license_gps_gga_data, vzAppStruct->gga, MEM_SIZE(license_data_t, license_gps_gga_data));
    memcpy(license_data->license_gps_rmc_data, vzAppStruct->rmc, MEM_SIZE(license_data_t, license_gps_rmc_data));
    license_data->len = CAL_FRAME_DATA_LEN_TO_NET(license_data_t);
    license_data->crc = htons(crc((uint8_t *)license_data + CRC_BASE_ADDR(license_data_t), sizeof(license_data_t) - MEM_SIZE(license_data_t, crc) - MEM_SIZE(license_data_t, head)));
}

static void calibrationV2DataInit(calibration_v2_data_t *calibration_data, VzAppStruct *vzAppStruct, uint8_t id, uint8_t unique_tag)
{
    memset(calibration_data, 0, sizeof(calibration_v2_data_t));
    headInit((head_t *)calibration_data, CALIBRATION_CMD_V2);
    time_bcd(calibration_data->calibration_v2_timebcd);
    calibration_data->calibration_v2_img_unique_tag = unique_tag;
    calibration_data->cam_id = id;
    calibration_data->calibration_v2_gps_type = 0x01;
    memcpy(calibration_data->calibration_v2_gps_gga_data, vzAppStruct->gga, MEM_SIZE(calibration_v2_data_t, calibration_v2_gps_gga_data));
    memcpy(calibration_data->calibration_v2_gps_rmc_data, vzAppStruct->rmc, MEM_SIZE(calibration_v2_data_t, calibration_v2_gps_rmc_data));
    calibration_data->len = CAL_FRAME_DATA_LEN_TO_NET(calibration_v2_data_t);
    calibration_data->crc = htons(crc((uint8_t *)calibration_data + CRC_BASE_ADDR(calibration_v2_data_t), sizeof(calibration_v2_data_t) - MEM_SIZE(license_data_t, crc) - MEM_SIZE(license_data_t, head)));
}

static void handleSmallAndBigPicSaveAndSend(TH_PlateResult *stDisc, VZ_PACKET_T *camData, VzAppStruct *vzAppStruct)
{
    uint8_t camId = vzAppStruct->vzCamId;
    if (isIvsResultRepeat(stDisc, camId) == true)
    {
        return;
    }

    if (stDisc->nType == LT_UNKNOWN)
    {
        LOG_INFO("未知车牌");
        return;
    }
    LOG_INFO("车牌可信度：%d%%；车牌号： %s", stDisc->nConfidence, stDisc->license);
    {
        // 播报语音
        if (camId == 0)
        {
            audioVzQian();
        }
        else if (camId == 1)
        {
            audioVzHou();
        }
    }

    // 识别车牌，上传车牌信息数据
    Packet *licenseDataBuf = PacketMalloc(sizeof(license_data_t));
    license_data_t license_data = {0};

    // gps_t gps = getLastGps();
    uint8_t imgNum = picNums++;
    licenseDataInit(&license_data, stDisc, vzAppStruct, camId, imgNum);

    // LOG_INFO("车牌信息上传");
    copy_packet(licenseDataBuf, sizeof(license_data_t), (uint8_t *)&license_data);
    othersEnqueue(licenseDataBuf);
    dispatchSendPendingQueue();

    // 上传图片信息
    // 跳过图片1
    VZ_PACKET_T *img1 = (VZ_PACKET_T *)camData;
    // LOG_DEBUG("图片1大小：%d", img1->stHead.unDataLen);
    // 获取图片2
    VZ_PACKET_T *img2 = (VZ_PACKET_T *)((uint8_t *)img1->acData + img1->stHead.unDataLen);
    // LOG_DEBUG("图片2大小：%d", img2->stHead.unDataLen);

    VZ_PACKET_T *imgSmall;
    VZ_PACKET_T *imgBig;
    if (img1->stHead.ucPacketType == 3)
    {
        imgSmall = img1;
        imgBig = img2;
    }
    else if (img2->stHead.ucPacketType == 3)
    {
        imgSmall = img2;
        imgBig = img1;
    }
    else
    {
        // 没找到小图，任取一张图
        imgSmall = img1;
        imgBig = img2;
    }

    uint8_t imgSmallFileName[PIC_FILENAME_MAX_LEN] = {0};
    uint8_t imgBigFileName[PIC_FILENAME_MAX_LEN] = {0};

    genPicFileName(imgSmallFileName, license_data.license_img_unique_tag, license_data.licese_timeBcd, camId, SMALL_PIC_TYPE);
    genPicFileName(imgBigFileName, license_data.license_img_unique_tag, license_data.licese_timeBcd, camId, BIG_PIC_TYPE);

    uint8_t imgSmallPath[128] = {0};
    uint8_t imgBigPath[128] = {0};

    sprintf(imgSmallPath, "%s/%s.jpg", IMG_SAVE_DIR, imgSmallFileName);
    sprintf(imgBigPath, "%s/%s.jpg", IMG_SAVE_DIR, imgBigFileName);

    saveImgs(imgSmallPath, imgSmall);
    saveImgs(imgBigPath, imgBig);

    insertPicToSqlite(imgBigFileName, BIG_PIC_TYPE, &license_data);
    insertPicToSqlite(imgSmallFileName, SMALL_PIC_TYPE, &license_data);

    // LOG_INFO("小图片大小：%d", imgSmall->stHead.unDataLen);
    // 泛文件上传图片
    // LOG_DEBUG("图片2大小：%d", imgBig->stHead.unDataLen);
    // FILE *p = fopen("imgSmall", "wb");
    // int ret = fwrite(imgSmall->acData, 1, imgSmall->stHead.unDataLen, p);
    // fflush(p);
    // LOG_INFO("写%d字节", ret);
    // FILE *p2 = fopen("imgBig", "wb");
    // ret = fwrite(imgBig->acData, 1, imgBig->stHead.unDataLen, p2);
    // fflush(p2);
    // LOG_INFO("写%d字节", ret);

    // 送小图
    if (getIsConnectToServ() == false)
    {
        LOG_ERROR("未连接上服务器，无法上送照片数据");
    }
    else
    {
        uint8_t *imgSmallBytes = (uint8_t *)malloc(imgSmall->stHead.unDataLen);
        memset(imgSmallBytes, 0, imgSmall->stHead.unDataLen);
        memcpy(imgSmallBytes, imgSmall->acData, imgSmall->stHead.unDataLen);

        CamDataStruct *imgSmallStruct = (CamDataStruct *)malloc(sizeof(CamDataStruct));
        imgSmallStruct->camData = imgSmallBytes;
        imgSmallStruct->camDataLen = imgSmall->stHead.unDataLen;
        imgSmallStruct->camId = camId;
        imgSmallStruct->uniqueTag = license_data.license_img_unique_tag;
        imgSmallStruct->picType = SMALL_PIC_TYPE;
        memcpy(imgSmallStruct->timeBcd, license_data.licese_timeBcd, BCD_TIME_LENGTH);

        vz_work_req_t *wr_rq_send_pic_small = (vz_work_req_t *)malloc(sizeof(vz_work_req_t));
        wr_rq_send_pic_small->buf = uv_buf_init((uint8_t *)imgSmallStruct, sizeof(CamDataStruct));
        uv_queue_work(getLoop(), (uv_work_t *)wr_rq_send_pic_small, sendPicWork, afterPicSendWorkCb);
    }
}

typedef struct
{
    uint8_t *camDataBase64;
    VzAppStruct *vzAppStruct;
    calibration_v2_data_t calibration_data;
} CalibrationV2ImgData;

static void coverBase64ToImgAndSaveToSql(uv_work_t *handle)
{
    CalibrationV2ImgData *tmp = (CalibrationV2ImgData *)handle->data;

    VzAppStruct *vzAppStruct = tmp->vzAppStruct;
    uint8_t camId = vzAppStruct->vzCamId;
    calibration_v2_data_t calibration_data = tmp->calibration_data;

    uint32_t dataLen = 0;
    uint8_t *imgUin8t = base64_decode(tmp->camDataBase64, &dataLen);

    uint8_t imgFileName[PIC_FILENAME_MAX_LEN] = {0};

    VZ_PACKET_T *img = (VZ_PACKET_T *)malloc(sizeof(VZ_PACKET_T) + dataLen);
    img->stHead.unDataLen = dataLen;
    memcpy(img->acData, imgUin8t, dataLen);

    genPicFileName(imgFileName, calibration_data.calibration_v2_img_unique_tag, calibration_data.calibration_v2_timebcd, camId, CALIBRATION_PIC_TYPE);

    uint8_t imgPath[128] = {0};
    sprintf(imgPath, "%s/%s.jpg", IMG_SAVE_DIR, imgFileName);
    saveImgs(imgPath, img);

    insertCalibrationToSqlite(imgFileName, &calibration_data);

    LOG_INFO("标定图片大小：%d", dataLen);

    free(imgUin8t);
    free(img);
};

static void afterCover(uv_work_t *handle, int32_t status)
{
    CalibrationV2ImgData *tmp = (CalibrationV2ImgData *)handle->data;
    free(tmp->camDataBase64);
    free(handle->data);
    free(handle);
}

static void handleCalibrationPicSaveAndSend(uint8_t *camDataBase64, VzAppStruct *vzAppStruct)
{
    uint8_t camId = vzAppStruct->vzCamId;
    // 上传标定数据
    Packet *calibrationDataBuf = PacketMalloc(sizeof(calibration_v2_data_t));
    calibration_v2_data_t calibration_data = {0};

    uint8_t imgNum = picNums++;
    calibrationV2DataInit(&calibration_data, vzAppStruct, camId, imgNum);

    LOG_INFO("标定v2信息上传");
    copy_packet(calibrationDataBuf, sizeof(calibration_v2_data_t), (uint8_t *)&calibration_data);
    othersEnqueue(calibrationDataBuf);
    dispatchSendPendingQueue();
    audioSuccessCalibrationV2();

    // 算上结束符\0
    uint32_t base64DataLen = strlen(camDataBase64) + 1;

    uv_work_t *req = (uv_work_t *)malloc(sizeof(uv_work_t));
    req->data = (void *)malloc(sizeof(CalibrationV2ImgData));
    CalibrationV2ImgData *tmp = (CalibrationV2ImgData *)req->data;

    tmp->camDataBase64 = (uint8_t *)malloc(base64DataLen);
    memset(tmp->camDataBase64, 0, base64DataLen);
    memcpy(tmp->camDataBase64, camDataBase64, base64DataLen);

    tmp->calibration_data = calibration_data;
    tmp->vzAppStruct = vzAppStruct;

    uv_queue_work(getLoop(), req, coverBase64ToImgAndSaveToSql, afterCover);
}

static void parseUvInit()
{
    // uv事件初始化
    static bool isUvInit = false;
    if (isUvInit == false)
    {
        uv_loop_t *loop = getLoop();
        int32_t index = 0;
        for (index = 0; index < MAX_LICENSE_TIMER; index++)
        {
            uv_timer_init(loop, &lastLicenseArray[index].vzResultTimer);
        }
        isUvInit = true;
    }
}

void parseIvsResult(TH_PlateResult *stDisc, VZ_PACKET_T *camData, VzAppStruct *vzAppStruct)
{
    parseUvInit();
    handleSmallAndBigPicSaveAndSend(stDisc, camData, vzAppStruct);
}

void parseSnapShotResult(uint8_t *camDataBase64, VzAppStruct *vzAppStruct)
{
    handleCalibrationPicSaveAndSend(camDataBase64, vzAppStruct);
}

static bool isMkImgDir = false;
static void saveImgs(uint8_t *path, VZ_PACKET_T *img)
{
    if (isMkImgDir == false)
    {
        bool result = mkdirCommon(IMG_SAVE_DIR);
        if (result == false)
        {
            return;
        }
        isMkImgDir = true;
    }

    uint32_t imgsNum = getFilesNum(IMG_SAVE_DIR);

    if (imgsNum > get_upload_pic_config()->max_pic_number)
    {
        dispatchDeletePicExec();
    }

    uv_loop_t *loop = getLoop();
    fs_write_t *openFsT = (fs_write_t *)malloc(sizeof(fs_write_t));

    uint8_t *imgBytes = (uint8_t *)malloc(img->stHead.unDataLen);
    memset(imgBytes, 0, img->stHead.unDataLen);
    memcpy(imgBytes, img->acData, img->stHead.unDataLen);

    openFsT->buf = uv_buf_init((uint8_t *)imgBytes, img->stHead.unDataLen);

    uv_fs_open(loop, (uv_fs_t *)openFsT, path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, openImgCb);
}

static void openImgCb(uv_fs_t *req)
{
    uv_loop_t *loop = getLoop();
    fs_write_t *openFsT = (fs_write_t *)req;

    int32_t result = openFsT->req.result;

    if (result < 0)
    {
        LOG_ERROR("图片存储-打开文件失败: %s", uv_strerror(result));
        goto FREE;
    }

    fs_write_t *writeFsT = (fs_write_t *)malloc(sizeof(fs_write_t));
    writeFsT->buf = openFsT->buf;
    writeFsT->req.data = (ssize_t *)malloc(sizeof(openFsT->req.result));
    memcpy(writeFsT->req.data, &openFsT->req.result, sizeof(openFsT->req.result));
    uv_fs_write(loop, &(writeFsT->req), result, &(writeFsT->buf), 1, -1, writeImgCb);
FREE:
    uv_fs_req_cleanup(&openFsT->req);
    free(openFsT);
    // cppcheck-suppress memleak
}

static void writeImgCb(uv_fs_t *req)
{
    uv_loop_t *loop = getLoop();
    fs_write_t *writeFsT = (fs_write_t *)req;
    int32_t result = writeFsT->req.result;

    int32_t open_result = *((ssize_t *)writeFsT->req.data);
    free(writeFsT->req.data);

    if (result < 0)
    {
        LOG_ERROR("图片存储-写文件失败: %s", uv_strerror(result));
    }

    uv_fs_t *fsyncFst = (uv_fs_t *)malloc(sizeof(fs_write_t));
    uv_fs_fsync(loop, fsyncFst, result, fsyncImgcb);

    fs_write_t *closeFsT = (fs_write_t *)malloc(sizeof(fs_write_t));
    closeFsT->buf = writeFsT->buf;

    uv_fs_close(loop, &(closeFsT->req), open_result, closeImgcb);

    uv_fs_req_cleanup(&writeFsT->req);
    free(writeFsT);
    // cppcheck-suppress memleak
}
static void fsyncImgcb(uv_fs_t *req)
{
    free(req);
}

static void closeImgcb(uv_fs_t *req)
{
    uv_loop_t *loop = getLoop();
    fs_write_t *closeFsT = (fs_write_t *)req;

    int32_t result = closeFsT->req.result;

    // LOG_INFO("图片存储-写文件成功");
    uv_fs_req_cleanup(&(closeFsT->req));
    free(closeFsT->buf.base);
    free(closeFsT);
}

// 图片帧其他数据初始化
static void picBlockFrameOthersInfoInit(pic_block_send_t *picBlock)
{
    headInit((head_t *)picBlock, PIC_UPLOAD_CMD);

    picBlock->len = CAL_FRAME_DATA_LEN_TO_NET(pic_block_send_t);
    picBlock->crc = htons(crc((uint8_t *)picBlock + CRC_BASE_ADDR(pic_block_send_t), sizeof(pic_block_send_t) - MEM_SIZE(pic_block_send_t, crc) - MEM_SIZE(pic_block_send_t, head)));
}

void afterPicSendWorkCb(uv_work_t *workReq, int32_t status)
{
    vz_work_req_t *wr_rq = (vz_work_req_t *)workReq;
    CamDataStruct *camStruct = (CamDataStruct *)wr_rq->buf.base;
    free(camStruct->camData);
    free(camStruct);
    free(wr_rq);
}

static uint16_t fileCode = 0;
void sendPicWork(uv_work_t *workReq)
{
    vz_work_req_t *pic_work = (vz_work_req_t *)workReq;

    pic_block_send_t picBlock;
    memset(&picBlock, 0, sizeof(pic_block_send_t));

    CamDataStruct *camStruct = (CamDataStruct *)pic_work->buf.base;
    uint8_t *img = camStruct->camData;
    uint32_t imgLen = camStruct->camDataLen;
    if (img == CAM_NO_DATA)
    {
        LOG_ERROR("读取到空的img指针， 直接返回");
        return;
    }

    uint16_t fileCrc = crc(img, imgLen);
    uint8_t *chedaojiNo = getChedaojiNo();

    uint16_t fileId = fileCode++;
    uint16_t blocSize = MEM_SIZE(pic_block_send_t, body);
    // 总片数计算
    uint16_t firstBlockSize = blocSize;
    // 总包数
    uint16_t blockAllNum = 1;
    // 当前包序号
    uint16_t blockNo = 0;
    uint16_t lastBytes = 0;
    // 计算总包数目
    if (imgLen < firstBlockSize)
    {
        blockAllNum = 1;
        lastBytes = imgLen;
    }
    else
    {
        blockAllNum += (uint16_t)ceil(((double)imgLen - (double)firstBlockSize) / (double)blocSize);
        lastBytes = (imgLen - firstBlockSize) % blocSize;
    }
    // LOG_INFO("分块总量：%d", blockAllNum);
    // 先送首片
    picBlock.file_id = htons(fileId);
    picBlock.block_num = htons(blockAllNum);
    picBlock.block_no = htons(blockNo++);
    switch (camStruct->picType)
    {
    case SMALL_PIC_TYPE:
    {
        picBlock.file_type = htons(0x9997);
        break;
    }
    case BIG_PIC_TYPE:
    {
        picBlock.file_type = htons(0x9998);
        break;
    }
    case CALIBRATION_PIC_TYPE:
    {
        picBlock.file_type = htons(0x9996);
        break;
    }
    default:
    {
        LOG_ERROR("无图片类型：%d。默认使用小图片", camStruct->picType);
        picBlock.file_type = htons(0x9998);
        break;
    }
    }
    picBlock.last_bytes = htons(lastBytes);
    picBlock.calculate_file_crc = htons(fileCrc);
    picBlock.pic_nums = camStruct->uniqueTag;
    picBlock.cam_id = camStruct->camId;

    memcpy(picBlock.pic_time_bcd, camStruct->timeBcd, BCD_TIME_LENGTH);

    memcpy(picBlock.body, img, firstBlockSize > imgLen ? imgLen : firstBlockSize);

    picBlockFrameOthersInfoInit(&picBlock);

    Packet *firstBlockTmp = PacketMalloc(sizeof(pic_block_send_t));
    copy_packet(firstBlockTmp, sizeof(pic_block_send_t), (uint8_t *)&picBlock);
    picsEnqueue(firstBlockTmp);
    dispatchSendPendingQueue();

    // 开始等信号量，一个信号量代表服务器收到上个包，这里是送接下来的包
    while (blockNo != blockAllNum)
    {
        Packet *blocPackkTmp = PacketMalloc(sizeof(pic_block_send_t));
        pic_block_send_t blockTmp = {0};
        memcpy(&blockTmp, &picBlock, sizeof(pic_block_send_t));
        memset(blockTmp.body, 0, MEM_SIZE(pic_block_send_t, body));

        if (blockNo == (blockAllNum - 1))
        {
            // 最后包
            memcpy(blockTmp.body, img + firstBlockSize + (blockNo - 1) * blocSize, lastBytes);
        }
        else if (blockNo == 1)
        {
            // 第二包
            memcpy(blockTmp.body, img + firstBlockSize, MEM_SIZE(pic_block_send_t, body));
        }
        else
        {
            // 其他中间包
            memcpy(blockTmp.body, img + firstBlockSize + (blockNo - 1) * blocSize, MEM_SIZE(pic_block_send_t, body));
        }

        blockTmp.block_no = htons(blockNo++);
        blockTmp.crc = htons(crc((uint8_t *)&blockTmp + CRC_BASE_ADDR(pic_block_send_t), sizeof(pic_block_send_t) - MEM_SIZE(pic_block_send_t, crc) - MEM_SIZE(pic_block_send_t, head)));

        copy_packet(blocPackkTmp, sizeof(pic_block_send_t), (uint8_t *)&blockTmp);
        // hzlog_info(userLog, blocPackkTmp->pkt_data, blocPackkTmp->pkt_len);
        picsEnqueue(blocPackkTmp);
        dispatchSendPendingQueue();
    }
PIC_END:
{
    uint8_t picName[PIC_FILENAME_MAX_LEN] = {0};
    genPicFileName(picName, camStruct->uniqueTag, camStruct->timeBcd, camStruct->camId, camStruct->picType);

    LOG_INFO("图片(%s: %s.jpg)id( 文件代码:%d )放置至发送队列完成（共%d分包），大小：%d", camStruct->picType == BIG_PIC_TYPE ? "大图" : camStruct->picType == SMALL_PIC_TYPE ? "小图"
                                                                                                                                                                             : "标定图片", //
             picName, fileId, blockAllNum, camStruct->camDataLen);                                                                                                                         //
}
}
